use super::HuffmanEntry;

#[derive(Debug, Clone)]
pub enum HuffmanTree {
    Leaf(HuffmanEntry),
    Medium{depth:u8, children:[Box<HuffmanTree>;2]},
}

impl HuffmanTree {
    pub fn new(literal_order:&Vec<HuffmanEntry>) -> Option<Box<Self>> {
        let mut result_vec:Vec<Box<HuffmanTree>> = Vec::new();
        for entry in literal_order.iter().filter(|entry|entry.number_of_bits!=0) {
            result_vec.push(Box::new(entry.clone().into()));
            while result_vec.len() > 1 {
                let tree0 = result_vec[result_vec.len() - 2].clone();
                let tree1 = result_vec[result_vec.len() - 1].clone();
                let father = Self::merge(tree0, tree1);
                if father.is_none() { break; }
                let father = father.unwrap();
                result_vec.truncate(result_vec.len() - 2);
                result_vec.push(father);
            }
        }
        result_vec.len().eq(&1).then_some(result_vec.pop().unwrap())
    }

    pub fn merge(tree0:Box<HuffmanTree>, tree1:Box<HuffmanTree>) -> Option<Box<Self>> {
        let depth0 = tree0.depth();
        let depth1 = tree1.depth();
        if depth0 != depth1 { return None; }
        let depth = depth0 - 1;
        let children = [tree0, tree1];
        let father = Box::new(Self::Medium { depth, children });
        Some(father)
    }

    pub fn depth(&self) -> u8 {
        match self {
            Self::Leaf(entry) => entry.number_of_bits,
            Self::Medium { depth, .. } => *depth,
        }
    }

    pub fn next(&self, bit:u8) -> &Self {
        match self {
            Self::Leaf(_) => self,
            Self::Medium { children, .. } => &children[bit as usize],
        }
    }
    
    pub fn literal(&self) -> Option<u8> {
        match self {
            Self::Leaf(entry) => Some(entry.literal),
            Self::Medium { .. } => None,
        }
    }
}

impl From<HuffmanEntry> for HuffmanTree {
    fn from(value: HuffmanEntry) -> Self {
        Self::Leaf(value)
    }
}